package ace.cmp.application.autoconfig;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author Caspar restful微服务架构，基础自动配置类 1. spring mvc
 */
@Slf4j
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore({JacksonAutoConfiguration.class})
@Configuration
public class JacksonAutoConfig implements WebMvcConfigurer {

  private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
  private static final String TIME_ZONE = "GMT+8";

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderGlobalCustomizer() {
    Jackson2ObjectMapperBuilderCustomizer customizer = jacksonObjectMapperBuilder -> {
      jacksonObjectMapperBuilder
          .serializerByType(Long.class, ToStringSerializer.instance)
          .serializerByType(Long.TYPE, ToStringSerializer.instance)
          .simpleDateFormat(DEFAULT_DATE_FORMAT)
          .timeZone(TIME_ZONE)
          .serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
          .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
          .propertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE)
          .featuresToDisable()
          // WRAP_ROOT_VALUE(false) : 序列化的json是否显示根节点
          .featuresToDisable(SerializationFeature.WRAP_ROOT_VALUE)
          // INDENT_OUTPUT(false): 允许或禁止是否以缩进的方式展示json
          .featuresToDisable(SerializationFeature.INDENT_OUTPUT)
          // 当类的一个属性外部无法访问(如：没有getter setter 的私有属性)，
          // 且没有annotation 标明需要序列化时，如果FAIL_ON_EMPTY_BEANS 是true 将会跑出异常，如果是false 则不会跑出异常
          .featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
          // 如果POJO 中有一个直接自我引用，在序列化的时候会抛出 com.fasterxml.jackson.databind.JsonMappingException
          .featuresToEnable(SerializationFeature.FAIL_ON_SELF_REFERENCES)
          // 如果序列化过程中，如果抛出 Exception 将会被包装，添加额外的上下文信息
          .featuresToEnable(SerializationFeature.WRAP_EXCEPTIONS)
          // value序列化日期以timestamps输出
          .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
          // key序列化日期以timestamps输出
          .featuresToDisable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)
          // 序列化日期是否带上时区ID
          .featuresToEnable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)
          // 序列化char[]时以json数组输出
          .featuresToDisable(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)
          // 序列化map时对key进行排序操作
          .featuresToEnable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)

          //  反序列化时,遇到未知属性(那些没有对应的属性来映射的属性,
          //  并且没有任何setter或handler来处理这样的属性)时是否引起结果失败。
          .featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
          // 反序列化时,遇到null属性映射在java基本数据类型（int或douuble）是否报异常。
          .featuresToDisable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
          // 反序列化时,遇到integer numbers属性映射在enum类型时，如果为true,numbers将不可以映射到enum中。
          .featuresToDisable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
          // 反序列化时，遇到类名错误或者map中id找不到时是否报异常。
          .featuresToEnable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)
          // 反序列化时，遇到json数据存在两个相同的key时是否报异常。
          .featuresToDisable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)
          // 反序列化时，遇到json属性字段为可忽略的是否报异常。
          .featuresToDisable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);
    };
    return customizer;
  }
}
